home *** CD-ROM | disk | FTP | other *** search
- /* DriverPrepRequest.c */
- /*
- * DriverPrepRequest.c
- * Copyright © 1994-95 Apple Computer Inc. All rights reserved.
- */
- /* .___________________________________________________________________________________.
- | These routines manage the details of calling PrepareMemoryForIO for a PBRead or |
- | or PBWrite request. The process is normally straightforward, but can become |
- | quite complex. The code flow for simple transfers is as follows: |
- | 1. PBRead/PBWrite calls PrepareNewDMATransfer |
- | 2. PrepareNewDMATransfer calls PrepareMemoryForIO and PrepareNextDMA |
- | 3. On return, the DMA parameters are sent to the hardware. |
- | The more complex code paths handle the following problems: |
- | If the user area is discontiguous, PrepareNextDMA will be re-called from the |
- | Primary Interrupt Service Routine to compute the next physical transfer |
- | area and length. In many drivers this would be pre-processed into a |
- | device-specific scatter-gather list. |
- | If PrepareMemoryForIO did not prepare the entire area, it must be re-called |
- | to handle partial preparation. In this case, the primary interrupt service |
- | routine queues a Software Task and exits the interrupt without restarting |
- | the device. When the Software Task runs, it calls PrepareMemoryForIO, |
- | re-calls PrepareNextDMA, and queues a Secondary Interrupt handler that |
- | restarts I/O. Error handling in this case is tricky, as an error must |
- | cause the device to abort the transfer (otherwise subsequent transfers |
- | won't be started. This is needed if the physical map is too small, if the |
- | device granularity is smaller than the transfer length, or -- in some cases |
- | if a virtual memory "page turn" forces partial preparation. Because of the |
- | latter, this driver MUST NOT be in the virtual memory paging process. |
- | The overall algorithm is given in the documentation of the DMA library. |
- | Functions: |
- | PrepareNewDMATransfer Called from driver mainline to initialize variables and |
- | prepare the first DMA sequence. |
- | PrepareNextDMATask Software Task queued from the primary interrupt to |
- | continue preparation. |
- | PrepareNextDMA Called after preparation and I/O to compute the next |
- | DMA physical address and transfer length. |
- | ConfigureThisPhysicalArea Performs the actual address/length computation. |
- | PrepareNextDMA returns a status value that controls the caller: |
- | noErr Success: start (or restart) I/O |
- | kPrepareMemoryRestart After a primary interrupt: the function queued a Software |
- | Task to handle partial preparation. The interrupt routine |
- | should exit without restarting I/O. When the Software Task |
- | completes, it will queue our Secondary Interrupt handler |
- | with this status to continue the I/O operation. |
- | scsiDataRunError Failure: The I/O table "done" state was set. This means the |
- | device wants to transmit more data than the application. |
- | The interrupt routine should halt the transfer. |
- | other error Failure from PrepareMemoryForIO. The interrupt routine |
- | should halt the transfer. |
- | The Software Task queues the common Secondary Interrupt Handler with status set |
- | as follows: |
- | kPrepareMemoryRestart Success after partial preparation: restart I/O |
- | If there is an error, the SCSI Script will eventually |
- | fail through its I/O rundown and return scsiDataRunError |
- | (The only error returned by PrepareMemoryForIO is paramErr |
- | which isn't too useful.) |
- .___________________________________________________________________________________.
- */
- #include "NCRDriverPrivate.h"
- /*
- * The current request will always be referenced through this local variable.
- */
- #define REQUEST (*perRequestDataPtr)
- #define PB (*((IOParam *) REQUEST.pb))
- #define SCSI (*((NCRSCSIParamPtr) PB.ioMisc))
- #define IOTABLE (REQUEST.scsiIOTable)
-
- OSErr DoInitialPreparation(
- PerRequestDataPtr perRequestDataPtr
- );
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * PrepareNewDMATransfer initializes a user I/O data transfer request. It initializes
- * the dma parameters, calls PrepareMemoryForIO, and specifies the first DMA address
- * and length. If this transfer does not support a data phase, it returns noErr without
- * preparing anything.
- */
- OSErr
- PrepareNewDMATransfer(
- PerRequestDataPtr perRequestDataPtr
- )
- {
- OSErr status;
- IOPreparationOptions options;
-
- Trace(PrepareNewDMATransfer);
- CLEAR(IOTABLE);
- IOTABLE.preparationID = kInvalidID; /* Marker for CheckpointIOTable */
- switch (SCSI.driverAction) {
- case kNCRDriverNoDataPhase:
- status = noErr;
- options = 0;
- break;
- case kNCRDriverInputAllowed:
- options = kIOIsInput;
- break;
- case kNCRDriverOutputAllowed:
- options = kIOIsOutput;
- break;
- default:
- options = 0;
- status = paramErr;
- break;
- }
- if (options != 0) { /* Preparation required */
- PB.ioActCount = 0; /* Nothing transferred yet */
- IOTABLE.options =
- ( options /* Input or output */
- | (0 * kIOMultipleRanges) /* No scatter-gather list */
- | (1 * kIOLogicalRanges) /* Logical addresses */
- | (0 * kIOMinimalLogicalMapping) /* No output logical map */
- | (1 * kIOShareMappingTables) /* Share with Kernel */
- | (0 * kIOCoherentDataPath) /* No fancy data path */
- );
- IOTABLE.addressSpace = REQUEST.addressSpaceID;
- IOTABLE.logicalMapping = NULL; /* We don't do logical xfer */
- IOTABLE.rangeInfo.range.base = PB.ioBuffer;
- IOTABLE.rangeInfo.range.length = PB.ioReqCount;
- IOTABLE.granularity = kNCRDriverMaxTransfer;
- IOTABLE.mappingEntryCount = REQUEST.scsiMapEntries;
- IOTABLE.physicalMapping = REQUEST.physicalMapTables;
- IOTABLE.state = 0; /* Clear done bit */
- IOTABLE.firstPrepared = 0;
- Timestamp('PMI+');
- status = PrepareMemoryForIO(&IOTABLE);
- Timestamp('PMI-');
- CheckStatus(status, "\pPrepareMemoryForIO initial");
- /*
- * The test for a non-zero length is redundant: PrepareMemoryForIO will
- * return a paramErr if a zero-length ioReqCount is provided.
- */
- if (status == noErr && IOTABLE.lengthPrepared == 0)
- status = scsiDataRunError;
- /*
- * After preparing the data area, initialize our private information.
- */
- status = DoInitialPreparation(perRequestDataPtr);
- }
- return (status);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * PrepareNextDMATask is the SoftwareTask that is executed in order to start the next
- * PrepareMemoryForIO request. The secondary interrupt routine previously checked the
- * "done" bit.
- */
- void
- PrepareNextDMATask(
- void *p1, /* perRequestDataPtr */
- void *p2 /* Unused */
- )
- {
- OSErr status;
- #define perRequestDataPtr ((PerRequestDataPtr) p1)
-
- Timestamp('SSI-');
- Trace(PrepareNextDMATask);
- UNUSED(p2);
- /*
- * Partial preparation. This should be extended to handle errors from
- * PrepareMemoryForIO. For example, we could stuff an error status into
- * the high short word in the p2 parameter.
- */
- Timestamp('PaP+');
- status = PrepareMemoryForIO(&REQUEST.scsiIOTable);
- Timestamp('PaP-');
- CheckStatus(status, "\pPrepareMemoryForIO partial prep");
- if (status == noErr) {
- status = DoInitialPreparation(perRequestDataPtr);
- CheckStatus(status, "\pInitial(re)Preparation");
- }
- /*
- * Restart I/O - we do this by calling our common Secondary Interrupt routine.
- * The status value tells the Secondary Interrupt routine to restart the device..
- */
- (void) NCRQueueSecondaryInterrupt(perRequestDataPtr, kPrepareMemoryRestart);
- #undef perRequestDataPtr
- }
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DoInitialPreparation is called from the driver mainline to start a transfer, and
- * from the software task to continue DMA transfers after a re-preparation.
- */
- OSErr
- DoInitialPreparation(
- PerRequestDataPtr perRequestDataPtr
- )
- {
- OSErr status;
-
- Trace(DoInitialPreparation);
- status = InitializeDMATransfer(&REQUEST.scsiIOTable, 0, &REQUEST.dmaTransferInfo);
- CheckStatus(status, "\pInitializeDMATransfer");
- return (status);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * PrepareNextDMA handles the details of a single DMA burst. Here, we assume that we
- * cannot build a physical scatter-gather table, but must construct each DMA transfer
- * by itself. It uses the following from the REQUEST record:
- * REQUEST.dmaTransferInfo This will be prepared by the initial call
- * and used by other calls.
- * REQUEST.thisDMATransfer The result: length will be non-zero if successful.
- * Return:
- * noErr Success -- start or continue the transfer.
- * scsiDataRunError Failure - no more data can be transfered.
- * other errors Failure.
- * Return scsiDataRunError if we're at the end of the transfer, or noErr if we
- * did the preparation. The Primary Interrupt routine will send a Software Interrupt,
- * other states will fail.
- */
- OSErr
- PrepareNextDMA(
- PerRequestDataPtr perRequestDataPtr
- )
- {
- OSErr status;
- Boolean isLogicalTransfer;
-
- Trace(PrepareNextDMA);
- status = PrepareDMATransfer(
- &REQUEST.dmaTransferInfo,
- &REQUEST.thisDMATransfer,
- &isLogicalTransfer
- );
- if (status == noErr
- && (isLogicalTransfer || REQUEST.thisDMATransfer.length == 0)) {
- LogString("\pPrepareDMATransfer bogus return");
- status = paramErr;
- }
- #if 1 && USE_LOG_LIBRARY
- WriteLogEntry(GLOBAL.logRecordPtr, 'pDMA',
- LogFormat4(kLogFormatSigned, kLogFormatAddress,
- kLogFormatUnsigned, kLogFormatString),
- (signed long) status,
- REQUEST.thisDMATransfer.base,
- REQUEST.thisDMATransfer.length,
- "\pNextDMA"
- );
- #endif
- if (status != noErr)
- REQUEST.thisDMATransfer.length = 0;
- return (status);
- }
-